fix(util): replace string parsing with AST in inspect_without_import #3504#3514
fix(util): replace string parsing with AST in inspect_without_import #3504#3514guptapratykshh wants to merge 4 commits intochaoss:mainfrom
Conversation
d466300 to
2e0e0e5
Compare
|
Great refactor. I've added some inline comments. Other than that, could you add a note about whether you used any AI tools (Claude, Copilot, etc.) for this PR? If so, what were they used for? You can see examples from other closed PRs. But otherwise looks like good PR. |
|
Thanks for the review. No, I did not use any AI tools for the implementation in this PR. |
MoralCode
left a comment
There was a problem hiding this comment.
Overall looks good! just a couple pieces of feedback
Thanks for doing this!
2e0e0e5 to
b88985a
Compare
|
id like to test this (run the unit tests) and add them to the CI, but overall looks good! |
| # We want any function that identifies itself as a 'phase' | ||
| if '_phase' in node.name: | ||
| phase_names.append(node.name) |
There was a problem hiding this comment.
does this filter for functions? or could someone create a top level variable matching this pattern and cause it to break too?
There was a problem hiding this comment.
No, this will not match top level variables. The check if isinstance(node, ast.FunctionDef): filters for function definitions only. Variables show up as ast.Assign or ast.AnnAssign nodes in AST tree, so this logic completely skips them.
I just added specific unit test, test_ignores_variables_with_matching_name, to verify this behavior and ensure safety.
d865924 to
623c6ce
Compare
b6ac1de to
8fdacaf
Compare
|
the changes have done , pleae have a look |
|
These new unit tests are not added to pyproject.toml such that they will be automatically tested. once thats done i will do a final review pass and merge this |
|
Hello! Just wanted to check in to see if you were still interested in helping the maintainers merge this PR. We noticed it has been a little while since this last had activity, and are considering closing it or taking it over if it remains in its current state. Please react to or reply to this to confirm your interest in the next 7 days or let us know if you are no longer interested in this so we can best prioritize everyone's contributions. Thanks! |
…haoss#3504 Signed-off-by: Pratyksh Gupta <pratykshgupta9999@gmail.com> Signed-off-by: guptapratykshh <pratykshgupta9999@gmail.com>
Signed-off-by: guptapratykshh <pratykshgupta9999@gmail.com>
Signed-off-by: Adrian Edwards <adredwar@redhat.com>
…rsion of the code Signed-off-by: Adrian Edwards <adredwar@redhat.com>
b374824 to
4ec5c4e
Compare
|
Taking this over. rebased |
|
i have tested this insofar as i was running the unit tests both before and after this change to validate both versions of the function are passing. Happy to split this PR to merge the unit test first if that would help de-risk this or demonstrate these assertions publicly |
shlokgilda
left a comment
There was a problem hiding this comment.
The core change is solid. The main thing blocking this for me is that most of the tests reimplement the AST logic inline with a different matching rule (in vs endswith), so they don't actually test the function. A small refactor to make the function accept an optional path would let all the edge case tests exercise the real code path.
|
|
||
| def weirdly_spaced_phase (repo_git): | ||
| pass | ||
| ''' |
There was a problem hiding this comment.
These inline tests use '_phase' in node.name (contains check), but the actual implementation uses node.name.endswith('_phase'). These are different matching rules. For eg. build_primary_phase_request contains _phase but doesn't end with it.
This matters because test_ignores_non_phase_functions explicitly asserts build_primary_phase_request IS found, which is correct under in but wrong under endswith. So the tests pass, but they're testing different logic than what ships.
There was a problem hiding this comment.
Maybe we refactor get_phase_names_without_import() to accept an optional source_path parameter (defaulting to the current hardcoded path). Then these tests can call the real function with a temp file instead of reimplementing the AST walk inline. That way any change to the matching logic is automatically covered.
There was a problem hiding this comment.
ah so this is probably because i changed it
There was a problem hiding this comment.
the endswith logic is a closer match to the behavior existing non AST version of the function i think. i probably didnt go through all the test cases in very much detail
| def test_handles_async_functions(self): | ||
| """Checks if async functions are also detected if they have the right name.""" | ||
| test_code = ''' | ||
| async def async_phase(repo_git): | ||
| pass | ||
|
|
||
| def sync_phase(repo_git): | ||
| pass | ||
| ''' | ||
|
|
||
| tree = ast.parse(test_code) | ||
| phase_names = [] | ||
|
|
||
| # AsyncFunctionDef is a different node type than FunctionDef | ||
| for node in ast.walk(tree): | ||
| if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and '_phase' in node.name: | ||
| phase_names.append(node.name) | ||
|
|
||
| assert 'async_phase' in phase_names | ||
| assert 'sync_phase' in phase_names | ||
| assert len(phase_names) == 2 |
There was a problem hiding this comment.
This test checks isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) but the implementation only checks ast.FunctionDef. Since this test does its own inline AST walk rather than calling the real function, it passes. But it's testing behavior the function doesn't actually have. If an async phase function were added to start_tasks.py, this test would still pass while the function silently misses it.
| for node in ast.walk(tree): | ||
| if isinstance(node, ast.FunctionDef): | ||
| # We want any function that identifies itself as a 'phase' | ||
| if node.name.endswith('_phase'): |
There was a problem hiding this comment.
Most of the edge case tests use '_phase' in node.name (contains) instead of endswith('_phase'). That means the tests are validating a broader matching rule than what this actually ships. More on that in the test file comments.
Also, this only checks ast.FunctionDef. If someone ever adds an async def phase function, it'd be silently skipped. Probably fine for now since there aren't any, but worth a comment or including ast.AsyncFunctionDef in the check.
|
yeah i agree with shlok, the tests need to not re-implement things. they should exercise the actual code paths being used. original contributor is no longer active to provide details on whether these tests were written with AI or not so we can only guess. Might make sense to have one of us, as maintainers reimplement them That said, this is more a tech debt reduction and generally quite low priority fix, so maybe not worth doing that for now. |
|
Hey, I've got the fixes ready on my fork but don't have push access to this branch. @MoralCode what's the best way to get these changes onto this branch? Should I open a separate PR instead? |
Description
augur/util/inspect_without_import.pywith theastmodule to robustly extract phase function names fromstart_tasks.py.tests/test_util/test_inspect_without_import.pyto verify the fix and ensure it handles various edge cases (indentation, decorators, multi-line definitions).This PR fixes #3504
Notes for Reviewers
Signed commits